From 1171cc00cd533ae9b497feeafe1218ac05453c93 Mon Sep 17 00:00:00 2001 From: Aaron Schulz Date: Fri, 9 Oct 2015 01:01:28 -0700 Subject: [PATCH] Inject MultiWriteBagOStuff addCallableUpdate() dependency Inject the DeferredUpdates::addCallableUpdate method via the ObjectCache. This brings it closer to being able to move to /libs. Change-Id: Ifa0d893002c3d709a4dc7346c263a92162274bd7 --- includes/objectcache/MultiWriteBagOStuff.php | 47 ++++++++++++------- includes/objectcache/ObjectCache.php | 3 ++ .../objectcache/MultiWriteBagOStuffTest.php | 3 +- 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/includes/objectcache/MultiWriteBagOStuff.php b/includes/objectcache/MultiWriteBagOStuff.php index a9304c1d9a..50dd215146 100644 --- a/includes/objectcache/MultiWriteBagOStuff.php +++ b/includes/objectcache/MultiWriteBagOStuff.php @@ -33,6 +33,8 @@ class MultiWriteBagOStuff extends BagOStuff { protected $caches; /** @var bool Use async secondary writes */ protected $asyncWrites = false; + /** @var callback|null */ + protected $asyncHandler; /** Idiom for "write to all backends" */ const ALL = INF; @@ -41,22 +43,23 @@ class MultiWriteBagOStuff extends BagOStuff { /** * $params include: - * - caches: A numbered array of either ObjectFactory::getObjectFromSpec - * arrays yeilding BagOStuff objects or direct BagOStuff objects. - * If using the former, the 'args' field *must* be set. - * The first cache is the primary one, being the first to - * be read in the fallback chain. Writes happen to all stores - * in the order they are defined. However, lock()/unlock() calls - * only use the primary store. - * - replication: Either 'sync' or 'async'. This controls whether writes to - * secondary stores are deferred when possible. Async writes - * require the HHVM register_postsend_function() function. - * Async writes can increase the chance of some race conditions - * or cause keys to expire seconds later than expected. It is - * safe to use for modules when cached values: are immutable, - * invalidation uses logical TTLs, invalidation uses etag/timestamp - * validation against the DB, or merge() is used to handle races. - * + * - caches: A numbered array of either ObjectFactory::getObjectFromSpec + * arrays yeilding BagOStuff objects or direct BagOStuff objects. + * If using the former, the 'args' field *must* be set. + * The first cache is the primary one, being the first to + * be read in the fallback chain. Writes happen to all stores + * in the order they are defined. However, lock()/unlock() calls + * only use the primary store. + * - replication: Either 'sync' or 'async'. This controls whether writes + * to secondary stores are deferred when possible. Async writes + * require setting 'asyncCallback'. HHVM register_postsend_function() function. + * Async writes can increase the chance of some race conditions + * or cause keys to expire seconds later than expected. It is + * safe to use for modules when cached values: are immutable, + * invalidation uses logical TTLs, invalidation uses etag/timestamp + * validation against the DB, or merge() is used to handle races. + * - asyncHandler: callable that takes a callback and runs it after the + * current web request ends. In CLI mode, it should run it immediately. * @param array $params * @throws InvalidArgumentException */ @@ -85,7 +88,14 @@ class MultiWriteBagOStuff extends BagOStuff { } } - $this->asyncWrites = isset( $params['replication'] ) && $params['replication'] === 'async'; + $this->asyncHandler = isset( $params['asyncHandler'] ) + ? $params['asyncHandler'] + : null; + $this->asyncWrites = ( + isset( $params['replication'] ) && + $params['replication'] === 'async' && + is_callable( $this->asyncHandler ) + ); } /** @@ -226,7 +236,8 @@ class MultiWriteBagOStuff extends BagOStuff { } else { // Secondary write in async mode: do not block this HTTP request $logger = $this->logger; - DeferredUpdates::addCallableUpdate( + call_user_func( + $this->asyncHandler, function () use ( $cache, $method, $args, $logger ) { if ( !call_user_func_array( array( $cache, $method ), $args ) ) { $logger->warning( "Async $method op failed" ); diff --git a/includes/objectcache/ObjectCache.php b/includes/objectcache/ObjectCache.php index 3665c11681..151bb065f9 100644 --- a/includes/objectcache/ObjectCache.php +++ b/includes/objectcache/ObjectCache.php @@ -178,6 +178,9 @@ class ObjectCache { return call_user_func( $params['factory'], $params ); } elseif ( isset( $params['class'] ) ) { $class = $params['class']; + if ( $class === 'MultiWriteBagOStuff' && !isset( $params['asyncHandler'] ) ) { + $params['asyncHandler'] = 'DeferredUpdates::addCallableUpdate'; + } return new $class( $params ); } else { throw new MWException( "The definition of cache type \"" diff --git a/tests/phpunit/includes/objectcache/MultiWriteBagOStuffTest.php b/tests/phpunit/includes/objectcache/MultiWriteBagOStuffTest.php index 2b66181c9f..ac52e1256f 100644 --- a/tests/phpunit/includes/objectcache/MultiWriteBagOStuffTest.php +++ b/tests/phpunit/includes/objectcache/MultiWriteBagOStuffTest.php @@ -18,7 +18,8 @@ class MultiWriteBagOStuffTest extends MediaWikiTestCase { $this->cache2 = new HashBagOStuff(); $this->cache = new MultiWriteBagOStuff( array( 'caches' => array( $this->cache1, $this->cache2 ), - 'replication' => 'async' + 'replication' => 'async', + 'asyncHandler' => 'DeferredUpdates::addCallableUpdate' ) ); } -- 2.20.1